/* * Copyright (C) 2015 Sebastian Daschner, sebastian-daschner.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.sebastian_daschner.jaxrs_analyzer.maven; import com.sebastian_daschner.jaxrs_analyzer.JAXRSAnalyzer; import com.sebastian_daschner.jaxrs_analyzer.LogProvider; import com.sebastian_daschner.jaxrs_analyzer.backend.Backend; import com.sebastian_daschner.jaxrs_analyzer.backend.swagger.SwaggerOptions; import org.apache.maven.artifact.Artifact; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.project.MavenProject; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.resolution.ArtifactRequest; import org.eclipse.aether.resolution.ArtifactResolutionException; import org.eclipse.aether.resolution.ArtifactResult; import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; import static java.util.Collections.singleton; import static java.util.stream.Collectors.joining; /** * Maven goal which analyzes JAX-RS resources. * * @author Sebastian Daschner * @goal analyze-jaxrs * @phase process-test-classes * @requiresDependencyResolution compile */ public class JAXRSAnalyzerMojo extends AbstractMojo { /** * The chosen backend format. Defaults to plaintext. * * @parameter default-value="plaintext" property="jaxrs-analyzer.backend" */ private String backend; /** * The domain where the project will be deployed. * * @parameter default-value="" property="jaxrs-analyzer.deployedDomain" */ private String deployedDomain; /** * The Swagger schemes. * * @parameter default-value="http" property="jaxrs-analyzer.swaggerSchemes" */ private String[] swaggerSchemes; /** * Specifies if Swagger tags should be generated. * * @parameter default-value="false" property="jaxrs-analyzer.renderSwaggerTags" */ private Boolean renderSwaggerTags; /** * The number at which path position the Swagger tags should be extracted. * * @parameter default-value="0" property="jaxrs-analyzer.swaggerTagsPathOffset" */ private Integer swaggerTagsPathOffset; /** * @parameter property="project.build.outputDirectory" * @required * @readonly */ private File outputDirectory; /** * @parameter property="project.build.sourceDirectory" * @required * @readonly */ private File sourceDirectory; /** * @parameter property="project.build.directory" * @required * @readonly */ private File buildDirectory; /** * @parameter property="project.build.sourceEncoding" */ private String encoding; /** * @parameter property="project" * @required * @readonly */ private MavenProject project; /** * The entry point to Aether. * * @component */ private RepositorySystem repoSystem; /** * The current repository/network configuration of Maven. * * @parameter property="repositorySystemSession" * @required * @readonly */ private RepositorySystemSession repoSession; /** * The project's remote repositories to use for the resolution of plugins and their dependencies. * * @parameter property="project.remotePluginRepositories" * @required * @readonly */ private List<RemoteRepository> remoteRepos; /** * Path, relative to outputDir, to generate resources * * @parameter default-value="jaxrs-analyzer" property="jaxrs-analyzer.resourcesDir" */ private String resourcesDir; @Override public void execute() throws MojoExecutionException { injectMavenLoggers(); // avoid execution if output directory does not exist if (!outputDirectory.exists() || !outputDirectory.isDirectory()) { LogProvider.info("skipping non existing directory " + outputDirectory); return; } final BackendType backendType = getBackendType(); final Backend backend = configureBackend(backendType); LogProvider.info("analyzing JAX-RS resources, using " + backend.getName() + " backend"); // add dependencies to analysis class path final Set<Path> classPaths = getDependencies(); LogProvider.debug("Dependency class paths are: " + classPaths); final Set<Path> projectPaths = singleton(outputDirectory.toPath()); LogProvider.debug("Project paths are: " + projectPaths); final Set<Path> sourcePaths = singleton(sourceDirectory.toPath()); LogProvider.debug("Source paths are: " + sourcePaths); handleSourceEncoding(); // create target sub-directory final File resourcesDirectory = Paths.get(buildDirectory.getPath(), resourcesDir).toFile(); if (!resourcesDirectory.exists() && !resourcesDirectory.mkdirs()) throw new MojoExecutionException("Could not create directory " + resourcesDirectory); final Path fileLocation = resourcesDirectory.toPath().resolve(backendType.getFileLocation()); LogProvider.info("Generating resources at " + fileLocation.toAbsolutePath()); // start analysis final long start = System.currentTimeMillis(); new JAXRSAnalyzer(projectPaths, sourcePaths, classPaths, project.getName(), project.getVersion(), backend, fileLocation).analyze(); LogProvider.debug("Analysis took " + (System.currentTimeMillis() - start) + " ms"); } private void handleSourceEncoding() { if (encoding != null && System.getProperty("project.build.sourceEncoding") == null) System.setProperty("project.build.sourceEncoding", encoding); } private BackendType getBackendType() { switch (backend.toLowerCase()) { case "plaintext": return BackendType.PLAINTEXT; case "asciidoc": return BackendType.ASCIIDOC; case "swagger": return BackendType.SWAGGER; default: throw new IllegalArgumentException("Backend " + backend + " not valid! Valid values are: " + Stream.of(BackendType.values()).map(Enum::name).map(String::toLowerCase).collect(joining(", "))); } } private Backend configureBackend(final BackendType backendType) throws IllegalArgumentException { final Map<String, String> config = new HashMap<>(); config.put(SwaggerOptions.SWAGGER_SCHEMES, Stream.of(swaggerSchemes).collect(joining(","))); config.put(SwaggerOptions.DOMAIN, deployedDomain); config.put(SwaggerOptions.RENDER_SWAGGER_TAGS, renderSwaggerTags.toString()); config.put(SwaggerOptions.SWAGGER_TAGS_PATH_OFFSET, swaggerTagsPathOffset.toString()); final Backend backend = JAXRSAnalyzer.constructBackend(backendType.name()); backend.configure(config); return backend; } private void injectMavenLoggers() { LogProvider.injectInfoLogger(getLog()::info); LogProvider.injectDebugLogger(getLog()::debug); LogProvider.injectErrorLogger(getLog()::error); } private Set<Path> getDependencies() throws MojoExecutionException { project.setArtifactFilter(a -> true); Set<Artifact> artifacts = project.getArtifacts(); if (artifacts.isEmpty()) { artifacts = project.getDependencyArtifacts(); } final Set<Path> dependencies = artifacts.stream().filter(a -> !a.getScope().equals(Artifact.SCOPE_TEST)).map(Artifact::getFile) .filter(Objects::nonNull).map(File::toPath).collect(Collectors.toSet()); final String analyzerVersion = project.getPluginArtifactMap().get("com.sebastian-daschner:jaxrs-analyzer-maven-plugin").getVersion(); // Java EE 7 and JAX-RS Analyzer API is needed internally dependencies.add(fetchDependency("javax:javaee-api:7.0")); dependencies.add(fetchDependency("com.sebastian-daschner:jaxrs-analyzer:" + analyzerVersion)); return dependencies; } private Path fetchDependency(final String artifactIdentifier) throws MojoExecutionException { ArtifactRequest request = new ArtifactRequest(); final DefaultArtifact artifact = new DefaultArtifact(artifactIdentifier); request.setArtifact(artifact); request.setRepositories(remoteRepos); LogProvider.debug("Resolving artifact " + artifact + " from " + remoteRepos); ArtifactResult result; try { result = repoSystem.resolveArtifact(repoSession, request); } catch (ArtifactResolutionException e) { throw new MojoExecutionException(e.getMessage(), e); } LogProvider.debug("Resolved artifact " + artifact + " to " + result.getArtifact().getFile() + " from " + result.getRepository()); return result.getArtifact().getFile().toPath(); } }